package com.atguigu.gmall.product.service.impl;

import com.atguigu.gmall.product.service.TestService;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

@Service
public class TestServiceImpl implements TestService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Override
    public String readLock() {
        /*
        使用步骤：
        RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");
        // 最常见的使用方法
        rwlock.readLock().lock();
        // 或
        rwlock.writeLock().lock();
         */
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("anyRWLock");
        //  给读取数据上锁！ 默认10秒钟 解锁！
        rwlock.readLock().lock(10,TimeUnit.SECONDS);

        //  从缓存读取数据
        String msg = this.redisTemplate.opsForValue().get("msg");

        //  不写解锁！
        return msg;
    }

    @Override
    public String writeLock() {
        //  读锁，写锁他们都要使用同一把锁！
        RReadWriteLock rwlock = redissonClient.getReadWriteLock("anyRWLock");
        //  上锁
        rwlock.writeLock().lock(10,TimeUnit.SECONDS);

        //  将uuid 写入到缓存！
        this.redisTemplate.opsForValue().set("msg",UUID.randomUUID().toString());
        //  为了看效果，暂不写解锁代码！
        return "写入完成.....";
    }

    @Override
    public void testLock() {
        /*
         1.  在缓存中初始化一个num  =  0； set num 0;
         2.  读取缓存中num ，如果有数据，则对其+1 ，并写入缓存！
         3.  如果没有数据，则直接返回！
         */
        RLock lock = redissonClient.getLock("lock");
        //  上锁
        lock.lock();
        try {
            //  业务逻辑：
            String num = this.redisTemplate.opsForValue().get("num");
            //  判断
            if (StringUtils.isEmpty(num)){
                return;
            }
            //  int i=1/0;
            //  转换为int 类型
            int numValue = Integer.parseInt(num);

            //  突然出现了一个 int i = 1/0;
            //  set num ++numValue
            this.redisTemplate.opsForValue().set("num",String.valueOf(++numValue));
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            //  解锁
            lock.unlock();
        }


    }

    //  不用 ex 时间自动删锁    finally delete 删锁 应该没啥问题吧
    //    @Override
    //    public void testLock() {
    //        //  使用setnx 加锁！
    //        Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", "atguigu");
    //        try {
    //            if (result){
    //                //  当线程上锁成功之后，要执行业务逻辑
    //                String num = this.redisTemplate.opsForValue().get("num");
    //                //  判断
    //                if (StringUtils.isEmpty(num)){
    //                    return;
    //                }
    //                //  int i=1/0;
    //                //  转换为int 类型
    //                int numValue = Integer.parseInt(num);
    //
    //                //  突然出现了一个 int i = 1/0;
    //                //  set num ++numValue
    //                this.redisTemplate.opsForValue().set("num",String.valueOf(++numValue));
    //            }else {
    //                try {
    //                    //  等待重试：
    //                    Thread.sleep(300);
    //                    //  自旋
    //                    testLock();
    //                } catch (InterruptedException e) {
    //                    e.printStackTrace();
    //                }
    //            }
    //        } catch (NumberFormatException e) {
    //            e.printStackTrace();
    //        } finally {
    //            //  删除锁
    //            this.redisTemplate.delete("lock");
    //        }
    //    }

//    @Override
//    public void testLock() {
//        /*
//        1.  在缓存中初始化一个num  =  0； set num 0;
//        2.  读取缓存中num ，如果有数据，则对其+1 ，并写入缓存！
//        3.  如果没有数据，则直接返回！
//         */
//        //   opsForValue 对应的String 数据类型方法  get key
//        //  使用setnx key value 命令来做分布式锁： sku:skuId:lock
//        //  Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", "atguigu");
//        //  set key value ex/px timeout nx/xx;
//        String uuid = UUID.randomUUID().toString();
//
//        Boolean result = this.redisTemplate.opsForValue().setIfAbsent("lock", uuid,3,TimeUnit.SECONDS);
//        //  result=true 表示上锁成功， result=false 表示上锁失败，说明里面有人操作！
//        if (result){
//            //  当线程上锁成功之后，要执行业务逻辑
//            String num = this.redisTemplate.opsForValue().get("num");
//            //  判断
//            if (StringUtils.isEmpty(num)){
//                return;
//            }
//
//            //  转换为int 类型
//            int numValue = Integer.parseInt(num);
//
//            //  突然出现了一个 int i = 1/0;
//            //  set num ++numValue
//            this.redisTemplate.opsForValue().set("num",String.valueOf(++numValue));
//
//            //  释放锁资源： 如果不删除锁：则会造成死锁，永远不会有其他线程进来！
//            //  删除锁的时候做判断：
//            //            if (uuid.equals(this.redisTemplate.opsForValue().get("lock"))){
//            //                //  如果缓存中的uuid  与 当前内存模型中的uuid 值相同，则删除key
//            //                //  index1 进来了，准备要执行删除lock 锁！ index1 的锁失效了， 在此时cpu 突然释放了执行权限，
//            //                //  index2 进来了，要去上锁，执行业务逻辑。 index2执行的过程中，cpu 可能会将执行权限交给index1, 删除index2 的锁！
//            //                this.redisTemplate.delete("lock");
//            //            }
//
//            //  声明一个lua 脚本：
//            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
//            //  如何执行lua 脚本删除锁！
//            //  声明一个RedisScript
//            DefaultRedisScript<Long> defaultRedisScript = new DefaultRedisScript<>();
//            defaultRedisScript.setScriptText(script);
//            defaultRedisScript.setResultType(Long.class);
//            //  这个脚本只在客户端传入的值和键的口令串相匹配时，才对键进行删除
//            this.redisTemplate.execute(defaultRedisScript, Arrays.asList("lock"),uuid);
//
//        }else {
//            try {
//                //  等待重试：
//                Thread.sleep(300);
//                //  自旋
//                testLock();
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//        }
//    }
}
